/**
 * @file wiz.c
 * @brief wizchip 基于STM32+FreeRTOS移植
 * @author wangh (wanghuan3037@fiberhome.com)
 * @version 1.0
 * @date 2020-11-09
 * @copyright Copyright (c) 2020  烽火通信
 ***************************************************************
 * @par 修改日志:
 * <table>
 * <tr><th>Date       <th>Version  <th>Author  <th>Description
 * <tr><td>2020-11-09 <td>1.0      <td>wangh   <td>内容
 * </table>
 */ 
#include <stdbool.h>
#include "wiz.h"
#include "wiz_socket.h"
#include "w5500.h"

#include "bsp_spi.h"
#include "project_board.h"

#include "FreeRTOS.h"
#include "task.h"
#include "timers.h"
#include "semphr.h"

#undef LOG_LEVEL_
#define LOG_LEVEL_ 	LOG_LVL_DEBUG
#include "bsp_log.h"

#if WIZ_USING_DHCP
#include "wizchip_dhcp.h"
#define WIZ_DHCP_SOCKET     7   ///< DHCP占用socket7
static TimerHandle_t dhcp_tick_timer;
#endif

#define IMR_SENDOK                     0x10
#define IMR_TIMEOUT                    0x08
#define IMR_RECV                       0x04
#define IMR_DISCON                     0x02
#define IMR_CON                        0x01

extern int8_t wiz_recv_notice_cb(uint8_t socket);       ///< 数据接收通知回调
extern int8_t wiz_closed_notice_cb(uint8_t socket);     ///< 断开通知回调
extern void wiz_linkDown_notice_cb( void );             ///< phy断开通知回调

#define WIZ_SPI_USE_LOCK    1
static Spi_t wzi_spi;
static Gpio_t wzi_cs;
#if WIZ_USE_RST_PIN
static Gpio_t wzi_rst;
#endif
#if WIZ_USE_ISR_PIN
static Gpio_t wzi_isr;
SemaphoreHandle_t wiz_isr_sem;  ///< 中断通知信号量
#endif
#if WIZ_SPI_USE_LOCK
SemaphoreHandle_t wiz_spi_lock; ///< SPI通信锁
#endif
bool wiz_init_ok = false;

/**
 * @brief 全局默认网络参数
 */
static wiz_NetInfo wiz_net_info = {
    .mac = {0x00, 0xF0, 0x01, 0x00, 0x00, 0x00},
    .ip = {10, 99, 20, 222},
    .sn = {255, 255, 0, 0},
    .gw = {10, 99, 1, 254},
    .dns = {114, 114, 114, 114},
    .dhcp = NETINFO_STATIC
};


static void spi_write_byte(uint8_t data)
{
    SpiInOut( &wzi_spi, data );
}

static uint8_t spi_read_byte(void)
{
    return SpiInOut( &wzi_spi, 0x00 );
}

static void spi_cris_enter(void)
{
#if WIZ_SPI_USE_LOCK
    xSemaphoreTake( wiz_spi_lock, portMAX_DELAY );
#else
    ENTER_CRITICAL( );
#endif
}

static void spi_cris_exit(void)
{
#if WIZ_SPI_USE_LOCK
    xSemaphoreGive(wiz_spi_lock);
#else
    EXIT_CRITICAL( );
#endif
}

static void spi_cs_select(void)
{
    GpioWrite( &wzi_cs, 0 );
}

static void spi_cs_deselect(void)
{
    GpioWrite( &wzi_cs, 1 );
}



#if WIZ_USE_RST_PIN
/**
 * @brief WIZnet芯片硬复位
 */
void wiz_reset(void)
{
    GpioWrite( &wzi_rst, 0 );
    vTaskDelay(2);
    GpioWrite( &wzi_rst, 1 );
    vTaskDelay(200);
}
#endif

#if WIZ_USE_ISR_PIN
/**
 * @brief wizchip中断处理函数
 */
static void wiz_isrHandler( uint8_t arg )
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    xSemaphoreGiveFromISR( wiz_isr_sem, &xHigherPriorityTaskWoken );
    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
#endif

/**
 * @brief wizchip数据接收通知任务
 */
void wiz_data_thread_entry(void *para)
{
    log_i("wiz_data_thread_entry start... \r\n");

    while (1)
    {
#if WIZ_USE_ISR_PIN
        if ( xSemaphoreTake( wiz_isr_sem, portMAX_DELAY ) == pdTRUE )
        {
            uint8_t ir = getIR();
            setIR(ir);

            if ((ir & IR_CONFLICT) == IR_CONFLICT)
            {
                setIR(IR_CONFLICT);
            }
            if ((ir & IR_UNREACH) == IR_UNREACH)
            {
                setIR(IR_UNREACH);
            }
#endif
            uint8_t sir = getSIR(); /* 获取socket中断寄存器 */
            for (uint8_t socket = 0; socket < WIZ_SOCKETS_NUM; socket++)
            {
                uint8_t sn_ir = 0;
                if (sir & (0x01 << socket))
                {
                    sn_ir = getSn_IR(socket);

                    if (sn_ir & Sn_IR_CON)      /* TCP成功建立连接 */
                    {
                        setSn_IR(socket, Sn_IR_CON);
                    }
                    if (sn_ir & Sn_IR_DISCON)   /* TCP收到FIN or FIN/ACK 包时 */
                    {
                        wiz_closed_notice_cb(socket);
                        setSn_IR(socket, Sn_IR_DISCON);
                    }
                    if (sn_ir & Sn_IR_RECV)
                    {
                        wiz_recv_notice_cb(socket);
                        setSn_IR(socket, Sn_IR_RECV);
                    }
                    if (sn_ir & Sn_IR_TIMEOUT)
                    {
                        setSn_IR(socket, Sn_IR_TIMEOUT); /* ARP或TCP传输超时 */
                    }
                }
            }

#if WIZ_USE_ISR_PIN
        }
#else
            vTaskDelay(10);
#endif
    }
}

/**
 * @brief 打印网络参数
 */
void wiz_display_netConfig( void )
{
    uint8_t tmpstr[6] = {0,};

    wiz_NetInfo netinfo;
    ctlnetwork(CN_GET_NETINFO, (void*) &netinfo);

    // Display Network Information
    ctlwizchip(CW_GET_ID,(void*)tmpstr);

    if(netinfo.dhcp == NETINFO_DHCP)
        log_d("\r\n===== %s NET CONF : DHCP =====\r\n",(char*)tmpstr);
    else
        log_d("\r\n===== %s NET CONF : Static =====\r\n",(char*)tmpstr);

    log_raw(" MAC : %02X:%02X:%02X:%02X:%02X:%02X\r\n", netinfo.mac[0], netinfo.mac[1], netinfo.mac[2], netinfo.mac[3], netinfo.mac[4], netinfo.mac[5]);
    log_raw(" IP : %d.%d.%d.%d\r\n", netinfo.ip[0], netinfo.ip[1], netinfo.ip[2], netinfo.ip[3]);
    log_raw(" GW : %d.%d.%d.%d\r\n", netinfo.gw[0], netinfo.gw[1], netinfo.gw[2], netinfo.gw[3]);
    log_raw(" SN : %d.%d.%d.%d\r\n", netinfo.sn[0], netinfo.sn[1], netinfo.sn[2], netinfo.sn[3]);
    log_raw(" DNS : %d.%d.%d.%d\r\n", netinfo.dns[0], netinfo.dns[1], netinfo.dns[2], netinfo.dns[3]);
    log_raw("=======================================\r\n");
}


#if WIZ_USING_DHCP
static void wiz_ip_assign(void)
{
    /* 获取动态DHCP获取的网络参数，并重新配置到芯片中 */
    getIPfromDHCP(wiz_net_info.ip);
    getGWfromDHCP(wiz_net_info.gw);
    getSNfromDHCP(wiz_net_info.sn);
    getDNSfromDHCP(wiz_net_info.dns);
    wiz_net_info.dhcp = NETINFO_DHCP;

    ctlnetwork(CN_SET_NETINFO, (void*) &wiz_net_info);
}

static void wiz_ip_conflict(void)
{
    log_d("conflict IP from DHCP.");
}

static void wiz_dhcp_time_handler(void *arg)
{
    extern void DHCP_time_handler(void);
    DHCP_time_handler();
}

/**
 * @brief DHCP工作
 * @param  dhcp_work   Param
 * @param  dhcp_work_dataParam
 */
static void wiz_dhcp_work( void )
{
#define WIZ_DHCP_WORK_RETRY     2

    uint8_t dhcp_times = 0;
    uint8_t data_buffer[600];
    uint8_t dhcp_status = 0;

    xTimerStart( dhcp_tick_timer, 0 );
    DHCP_init(WIZ_DHCP_SOCKET, data_buffer);

    while (1)
    {
        /* DHCP 启动, 返回 DHCP_IP_LEASED 表示租赁成功 */
        dhcp_status = DHCP_run();

        switch (dhcp_status)
        {
        case DHCP_IP_ASSIGN:
        case DHCP_IP_CHANGED:
        {
            /* 已分配 */
            log_d("DHCP_IP_CHANGED.\r\n");
            wiz_display_netConfig();
            break;
        }
        case DHCP_IP_LEASED:    /* 租赁成功 */
        {
            DHCP_stop();
            xTimerStop( dhcp_tick_timer, 0 );
            log_d("DHCP_IP_LEASED.\r\n");
            wiz_display_netConfig();
            return;
        }
        case DHCP_FAILED:
        {
            dhcp_times++;
            break;
        }
        case DHCP_STOPPED:
        {
            dhcp_times = WIZ_DHCP_WORK_RETRY;
            break;
        }
        default:
            break;
        }

        if (dhcp_times >= WIZ_DHCP_WORK_RETRY)
        {
            DHCP_stop();
            xTimerStop( dhcp_tick_timer, 0 );
            log_w("DHCP ip lease Failed, then config net static param....");
            
            break;
        }
        vTaskDelay(1);
    }
}

/**
 * @brief 网络DHCP配置
 * @return int8_t 0:成功
 */
static int8_t wiz_network_dhcp( void )
{
    setSHAR(wiz_net_info.mac);
    setSn_RXBUF_SIZE(WIZ_DHCP_SOCKET, 0x02);
    setSn_TXBUF_SIZE(WIZ_DHCP_SOCKET, 0x02);
    /* 注册IP分配和IP冲突回调 */
    reg_dhcp_cbfunc(wiz_ip_assign, wiz_ip_assign, wiz_ip_conflict);

    dhcp_tick_timer = xTimerCreate("dhcp_tick", pdMS_TO_TICKS(1000), pdTRUE, NULL, wiz_dhcp_time_handler);
    if(dhcp_tick_timer == NULL)
    {
        log_e("dhcp_tick_timer create error\r\n");
        return -1;
    }

    return 0;
}

#endif



/**
 * @brief 网络参数初始化
 * @param  net_info    网络配置
 * @return int8_t 0：成功
 */
static int8_t wiz_network_init( void )
{
extern void GetMcuId( uint8_t *id );
    uint8_t id[12];
    GetMcuId(id);
    wiz_net_info.mac[3] = id[0]|id[1]|id[2]|id[3];
    wiz_net_info.mac[4] = id[4]|id[5]|id[6]|id[7];
    wiz_net_info.mac[5] = id[8]|id[9]|id[10]|id[11];
    ctlnetwork(CN_SET_NETINFO, (void*) &wiz_net_info);

#if WIZ_USING_DHCP
    if( wiz_network_dhcp() < 0 )
    {
        log_e("wiz_network_dhcp error\r\n");
        return -1;
    }
#endif

    return 0;
}


#if WIZ_USING_DNS
static void wiz_dns_time_handler(void *arg)
{
    extern void DNS_time_handler(void);
    DNS_time_handler();
}
#endif

#if WIZ_SAFE_DESIGN
static void wiz_safe_time_handler(void *arg)
{
    extern void WIZ_time_handler(void);
    WIZ_time_handler();
}
#endif

/**
 * @brief wizchip link 状态轮询任务
 */
void wiz_link_status_thread_entry(void *para)
{
    log_i("wiz_link_status_thread_entry start... \r\n");

    static uint8_t last_link_state = PHY_LINK_OFF;
    while (1)
    {
        uint8_t link_state = wizphy_getphylink();
        if( (link_state == PHY_LINK_ON) && (link_state != last_link_state) )
        {
            log_i("phy link status becomes link up\r\n");
#if WIZ_USING_DHCP
            if (wizchip_getDHCP() == NETINFO_DHCP)
                wiz_dhcp_work();
#endif
        }
        else if( (link_state == PHY_LINK_OFF) && (link_state != last_link_state) )
        {
            log_i("phy link status becomes link down\r\n");
            wiz_linkDown_notice_cb();
        }
        last_link_state = link_state;

        vTaskDelay(2000);
    }
}

/**
 * @brief WIZnet芯片配置 \n
 * @note 芯片异常复位时可调用
 * @param[in]  net_info 网络参数结构体（MAC、IP、网关、子网掩码、DNS、DHCP）
 * @return int8_t 
 * - @b Sucess : 0 \n
 * - @b Fail : -1
 */
int8_t wiz_config( wiz_NetInfo *net_info )
{
    int8_t ret = 0;

#if WIZ_USE_RST_PIN
    // wiz_reset();
#endif

    log_a("SIP is valid then reconfig wizchip...\r\n");

    uint8_t idx = 0;
    setSIMR(0xff);  /* 使能socket(0-7)中断 */

    for (idx = 0; idx < WIZ_SOCKETS_NUM; idx++)
    {
        /* 设置socket TX/RX buffersize */
        setSn_RXBUF_SIZE(idx, 0x02);
        setSn_TXBUF_SIZE(idx, 0x02);

        /* 设置 socket 中断支持 */
        setSn_IMR(idx, (IMR_TIMEOUT | IMR_RECV | IMR_DISCON));

        /* 设置在线验证心跳包 50s */
        // setSn_KPALVTR(idx, 10);
    }

    /* 设置ARP或TCP等超时时间和重试次数 */
    wiz_NetTimeout netTimeout;
    netTimeout.retry_cnt = 3;
    netTimeout.time_100us = 2000;
    ctlnetwork(CN_SET_TIMEOUT, (void*) &netTimeout);

    if (net_info == NULL)   /* 设置保存的网络参数 */
        ret = ctlnetwork(CN_SET_NETINFO, (void*) &wiz_net_info);
    else
        ret = wiz_set_network(net_info);

    return ret;
}

/**
 * @brief wizchip初始化
 * @return int8_t 
 * - @b Sucess : 0 \n
 * - @b Fail : -1
 */
int8_t wiz_init( void )
{
    if (wiz_init_ok == true)
    {
        log_w("W5500 is already initialized.");
        return -1;
    }

    /********* SPI、IO 设备初始化 **************/
    if (BSP_SUCCESS != SpiInit( &wzi_spi, SPI_2, SPI2_MOSI, SPI2_MISO, SPI2_SCLK, NC ))
    {
        log_e("W5500 SpiInit error.");
        return -1;
    }
    GpioInit( &wzi_cs, SPI2_NSS, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 );

    /* 中断引脚初始化 */
#if WIZ_USE_ISR_PIN
    wiz_isr_sem = xSemaphoreCreateBinary();    /* TODO:改进可考虑使用消息队列来避免中断遗漏 */
    if(wiz_isr_sem == NULL)
    {
        log_e("wiz_isr_sem create error\r\n");
        return -1;
    }
    GpioInit( &wzi_isr, W5500_INT, PIN_INPUT, PIN_PUSH_PULL, PIN_PULL_UP, 1 );    /* 中断引脚 */
    GpioSetInterrupt( &wzi_isr, IRQ_FALLING_EDGE, IRQ_MEDIUM_PRIORITY, wiz_isrHandler );
#endif

#if WIZ_USE_RST_PIN
    GpioInit( &wzi_rst, W5500_RST, PIN_OUTPUT, PIN_PUSH_PULL, PIN_NO_PULL, 1 );   /* 复位引脚 */
    wiz_reset();
#endif

    /* 创建数据接收通知任务 */
    if( xTaskCreate( wiz_data_thread_entry, "wiz", WIZ_DATA_STK_SIZE, NULL, WIZ_DATA_TASK_PRIO, NULL) != pdPASS)
    {
        log_e("wiz_data_thread_entry create error\r\n");
        return -1;
    }

#if WIZ_SPI_USE_LOCK
    wiz_spi_lock = xSemaphoreCreateBinary();    /* 初始时信号值为0 */
    if(wiz_spi_lock == NULL)
    {
        log_e("wiz_spi_lock create error\r\n");
        return -1;
    }
    xSemaphoreGive(wiz_spi_lock);
#endif

    /************ 驱动适配回调函数注册 ****************/
    reg_wizchip_cris_cbfunc( spi_cris_enter, spi_cris_exit );   /* 临界区函数注册 */
    reg_wizchip_cs_cbfunc( spi_cs_select, spi_cs_deselect );    /* 片选操作函数注册 */
    reg_wizchip_spi_cbfunc( spi_read_byte, spi_write_byte );    /* spi字节读写函数注册 */


    /************** 网络参数设置 **********************/
    if( wiz_network_init( ) < 0 )
    {
        log_e("wiz_network_init error\r\n");
        return -1;
    }


    /************* socket 寄存器设置 ******************/
    wiz_config(NULL);


#if WIZ_USING_DNS
    TimerHandle_t dns_tick_timer = xTimerCreate("dns_tick", pdMS_TO_TICKS(1000), pdTRUE, NULL, wiz_dns_time_handler);
    if(dns_tick_timer == NULL)
    {
        log_e("dns_tick create error\r\n");
        return -1;
    }
    xTimerStart( dns_tick_timer, 0 );
#endif

#if WIZ_SAFE_DESIGN
    /* 接口安全设计，接口安全超时定时器(1ms) */
    TimerHandle_t wiz_safe_timer = xTimerCreate("wiz_tick", pdMS_TO_TICKS(1), pdTRUE, NULL, wiz_safe_time_handler);
    if(wiz_safe_timer == NULL)
    {
        log_e("wiz_safe_timer create error\r\n");
        return -1;
    }
    xTimerStart( wiz_safe_timer, 0 );
#endif

    /* 创建 wizchip link 状态轮询任务 */
#if WIZ_USING_DHCP
    if( xTaskCreate( wiz_link_status_thread_entry, "wiz_stat", WIZ_STAT_STK_SIZE, NULL, WIZ_STAT_TASK_PRIO, NULL) != pdPASS)
#else
	if( xTaskCreate( wiz_link_status_thread_entry, "wiz_stat", WIZ_STAT_STK_SIZE, NULL, WIZ_STAT_TASK_PRIO, NULL) != pdPASS)
#endif
    {
        log_e("wiz_link_status_thread_entry create error\r\n");
        return -1;
    }

    wiz_init_ok = true;
    return 0;
}

/**
 * @brief 设置网络参数
 * @param[in]  net_info 网络参数结构体（MAC、IP、网关、子网掩码、DNS、DHCP）
 * @return int8_t 
 * - @b Sucess : 0 \n
 * - @b Fail : -1
 */
int8_t wiz_set_network(wiz_NetInfo *net_info)
{
    if (!wiz_init_ok)
        return -1;

    if (net_info == NULL)
    {
        ctlnetwork(CN_SET_NETINFO, (void*) &wiz_net_info);
        return 0;
    }

    if (net_info->mac[0] != 0)
    {
        setSHAR(net_info->mac);
        memcpy(wiz_net_info.mac, net_info->mac, 6);
    }

    if (net_info->ip[0] != 0)
    {
        setSIPR(net_info->ip);
        memcpy(wiz_net_info.ip, net_info->ip, 4);
    }

    if (net_info->gw[0] != 0)
    {
        setGAR(net_info->gw);
        memcpy(wiz_net_info.gw, net_info->gw, 4);
    }

    if (net_info->sn[0] != 0)
    {
        setSUBR(net_info->sn);
        memcpy(wiz_net_info.sn, net_info->sn, 4);
    }

    if (net_info->dns[0] != 0)
    {
        wizchip_setDNS(net_info);
        memcpy(wiz_net_info.dns, net_info->dns, 4);
    }

    if ((net_info->dhcp > 0) && (net_info->dhcp < 3))
    {
        wizchip_setDHCP(net_info->dhcp);
        wiz_net_info.dhcp = net_info->dhcp;
    }

    return 0;
}

/**
 * @brief 获取网络参数
 * @param[in]  net_info 网络参数结构体（MAC、IP、网关、子网掩码、DNS、DHCP）
 * @return int8_t 
 * - @b Sucess : 0 \n
 * - @b Fail : -1
 */
int8_t wiz_get_network(wiz_NetInfo *net_info)
{
    if ((!wiz_init_ok) || (net_info == NULL))
        return -1;
    
    wizchip_getnetinfo(net_info);

    return 0;
}


/*********** (C) COPYRIGHT 2020 FiberHome *****END OF FILE****/
